package my.edu.model.information.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.PageVO;
import my.edu.common.exception.BillsException;
import my.edu.common.exception.CustomerException;
import my.edu.common.exception.Order.SalesException;
import my.edu.common.utils.PageUtils;
import my.edu.constant.CustomerPaymentTerms;
import my.edu.model.User.entity.User;
import my.edu.model.commodity.entity.PriceField;
import my.edu.model.commodity.service.IPriceFieldService;
import my.edu.model.information.entity.*;
import my.edu.model.information.entity.VO.CustomerVO;
import my.edu.model.information.entity.dto.CustomerDTO;
import my.edu.model.information.entity.dto.CustomerSearchDTO;
import my.edu.utils.ChildrenLoader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import my.edu.model.information.mapper.CustomerMapper;

import java.beans.Transient;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> implements ICustomerService {

    @Autowired
    private ICustomerCategoryService customerCategoryService;
    @Autowired
    private CustomerPaymentTerms customerPaymentTerms;
    @Autowired
    private IRouteService routeService;
    @Autowired
    private IPriceFieldService priceFieldService;
    @Autowired
    private ICustomerReceivableBalanceDetailService customerReceivableBalanceDetailService;

//    @Override
//    public Customer getByNo(String no) {
//        Optional<Customer> customer = lambdaQuery()
//                .eq(Customer::getCustomerNo, no)
//                .oneOpt();
//        if (customer.isEmpty()) {
//            throw new CanNotFoundException(MessageConstant.CAN_NOT_FOUND);
//        }
//        return customer.get();
//    }

    @Override
    public void savenew(CustomerDTO customerDTO) {
        //检查客户类别是否存在
//        CustomerCategory category = customerCategoryService.getById(customerDTO.getCustomerCategoryId());
//        if (category == null || category.getStatus().equals(0)) {
//            throw new CategoryException(MessageConstant.CATEGORIES_NOT_AVAILABLE);
//        }
        //检查付款条件
        if (customerDTO.getPaymentTerms() != null && !customerPaymentTerms.getPaymentTerms().contains(customerDTO.getPaymentTerms())) {
            throw new CustomerException(MessageConstant.PAYMENT_TERMS_NOT_AVAILABLE);
        }
        //检查线路
        if (customerDTO.getAssociatedRouteId() != null) {
            Route route = routeService.getById(customerDTO.getAssociatedRouteId());
            if (route == null || route.getStatus().equals(0)) {
                throw new CustomerException(MessageConstant.ROUTE_NOT_AVAILABLE);
            }
        }
        //价格字段
        if (customerDTO.getPriceFieldId() != null) {
            PriceField priceField = priceFieldService.getById(customerDTO.getPriceFieldId());
            if (priceField == null) throw new CustomerException("价格字段不存在");

        }
        Customer customer = BeanUtil.copyProperties(customerDTO, Customer.class);
        customer.setReceivableBalance(BigDecimal.ZERO);
        customer.setStatus(1);
        save(customer);
    }

    @Override
    public void updateByDTO(CustomerDTO customerDTO) {
        Customer customer = BeanUtil.copyProperties(customerDTO, Customer.class);
        updateById(customer);
//        lambdaUpdate()
//                .eq(Customer::getCustomerNo, customer.getCustomerNo())
//                .update(customer);
    }

    @Override
    public PageVO<CustomerVO> listAll(PageDTO pageDTO, CustomerSearchDTO customerSearchDTO) {
        MPJLambdaWrapper<Customer> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(Customer.class, CustomerVO.class)
                //.selectAs(CustomerCategory::getCategoryName, CustomerVO::getCustomerCategoryName)
                .selectAs(Route::getRouteName, CustomerVO::getAssociatedRoute)
                .selectAs(User::getName, CustomerVO::getSalespersonName)
                .selectAs(PriceField::getFieldName, CustomerVO::getPriceFieldName)
                //.selectCollection(CustomerReceivableBalanceDetail.class, CustomerVO::getCustomerReceivableBalanceDetails)
                //.leftJoin(CustomerCategory.class, CustomerCategory::getId, Customer::getCustomerCategoryId)
                //.leftJoin(CustomerReceivableBalanceDetail.class, CustomerReceivableBalanceDetail::getCustomer_id, Customer::getId)
                .leftJoin(Route.class, Route::getId, Customer::getAssociatedRouteId)
                .leftJoin(User.class, User::getId, Customer::getSalespersonId)
                .leftJoin(PriceField.class, PriceField::getId, Customer::getPriceFieldId)
                .like(customerSearchDTO.getCustomerNo() != null, Customer::getCustomerNo, customerSearchDTO.getCustomerNo())
                .like(customerSearchDTO.getCustomerName() != null, Customer::getCustomerName, customerSearchDTO.getCustomerName())
                .like(customerSearchDTO.getSalespersonName() != null, Customer::getSalesperson, customerSearchDTO.getSalespersonName());

        // 1. 分页主表 + 联表字段
        Page<CustomerVO> customerVOPage = baseMapper.selectJoinPage(
                new Page<>(pageDTO.getPage(), pageDTO.getSize()),
                CustomerVO.class,
                wrapper
        );

        // 2. 批量获取子表数据，并按 customerId 分组填充
        if (CollUtil.isNotEmpty(customerVOPage.getRecords())) {
            ChildrenLoader.loadAndFill(
                    customerVOPage.getRecords(),
                    CustomerVO::getId,
                    ids -> customerReceivableBalanceDetailService.list(
                            Wrappers.<CustomerReceivableBalanceDetail>lambdaQuery()
                                    .in(CustomerReceivableBalanceDetail::getCustomer_id, ids)
                                    .orderByDesc(CustomerReceivableBalanceDetail::getId)
                    ),
                    CustomerReceivableBalanceDetail::getCustomer_id,
                    CustomerVO::setCustomerReceivableBalanceDetails);
        }
        return PageUtils.toPage(customerVOPage);
    }
    @Override
    public CustomerVO getVOById(String id) {
        MPJLambdaWrapper<Customer> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(Customer.class, CustomerVO.class)
                .selectAs(CustomerCategory::getCategoryName, CustomerVO::getCustomerCategoryName)
                .selectAs(Route::getRouteName, CustomerVO::getAssociatedRoute)
                .selectAs(User::getName, CustomerVO::getSalespersonName)
                .selectAs(PriceField::getFieldName, CustomerVO::getPriceFieldName)
                .leftJoin(CustomerCategory.class, CustomerCategory::getId, Customer::getCustomerCategoryId)
                .leftJoin(Route.class, Route::getId, Customer::getAssociatedRouteId)
                .leftJoin(User.class, User::getId, Customer::getSalespersonId)
                .leftJoin(PriceField.class, PriceField::getId, Customer::getPriceFieldId)
                .eq(Customer::getId, id);
        CustomerVO customerVOS = baseMapper.selectJoinOne(CustomerVO.class, wrapper);
        return customerVOS;
    }

    // 增加总的客户应收款和明细
    @Override
    @Transient
    public void addReceivableBalanceDetails(CustomerReceivableBalanceDetail receivableBalance) {
        Customer customer = chackCustomer(receivableBalance.getCustomer_id());
        if (receivableBalance.getReceivable_balance().compareTo(BigDecimal.ZERO) == 0) return;
        lambdaUpdate()
                .setSql("receivable_balance = receivable_balance + " + receivableBalance.getReceivable_balance())
                .eq(Customer::getId, customer.getId())
                .update();
        customerReceivableBalanceDetailService.save(receivableBalance);
    }

    // 检查客户
    private Customer chackCustomer(Long customerId) {
        Customer customer = getById(customerId);
        if (customer == null) throw new SalesException(MessageConstant.Customer_NOT_FOUND);
        if (customer.getStatus() == null || customer.getStatus().equals(0))
            throw new SalesException(MessageConstant.Customer_NOT_AVAILABLE);
        return customer;
    }

    // 删除客户应收款明细
    @Override
    @Transient
    public void subReceivableBalanceDetails(List<CustomerReceivableBalanceDetail> receivableBalances) {
        String billsType = receivableBalances.get(0).getBillsType();
        //获取客户id对应的欠款数额
        HashMap<Long, BigDecimal> idToBalance = new HashMap<>();
        for (CustomerReceivableBalanceDetail receivableBalance : receivableBalances) {
            chackCustomer(receivableBalance.getCustomer_id());
            BigDecimal bigDecimal = idToBalance.getOrDefault(receivableBalance.getCustomer_id(), BigDecimal.ZERO);
            BigDecimal add = bigDecimal.add(receivableBalance.getReceivable_balance());
            idToBalance.put(receivableBalance.getCustomer_id(), add);
        }
        //获取单据id并删除对应的欠款详情
        List<Long> idList = receivableBalances.stream()
                .map(CustomerReceivableBalanceDetail::getBill_id).toList();
        customerReceivableBalanceDetailService.remove(Wrappers.<CustomerReceivableBalanceDetail>lambdaQuery()
                .in(CustomerReceivableBalanceDetail::getBill_id, idList));
        //减去对应的欠款数额
        for (Map.Entry<Long, BigDecimal> entry : idToBalance.entrySet()) {
            Long key = entry.getKey();
            BigDecimal value = entry.getValue();
            subReceivableBalance(key, value);
        }

    }

    // 减少总的客户应收款
    @Transient
    public void subReceivableBalance(Long id, BigDecimal value) {
        if (value.compareTo(BigDecimal.ZERO) == 0) return;
        lambdaUpdate()
                .setSql("receivable_balance = receivable_balance - " + value)
                .eq(Customer::getId, id)
                .update();
    }

    @Override
    public void setStatus(List<Long> longs, int status) {
        update(Wrappers.<Customer>lambdaUpdate()
                .set(Customer::getStatus, status)
                .in(Customer::getId, longs));
    }

    //更新客户应收款
    @Override
    public void updateReceivableBalanceDetails(CustomerReceivableBalanceDetail receivableBalance) {
        //检查客户
        Customer customer = chackCustomer(receivableBalance.getCustomer_id());
        //删去对应欠款详情
        CustomerReceivableBalanceDetail customerReceivableBalanceDetail = customerReceivableBalanceDetailService.getOne(
                Wrappers.<CustomerReceivableBalanceDetail>lambdaQuery()
                        .eq(CustomerReceivableBalanceDetail::getBill_id, receivableBalance.getBill_id()));
        customerReceivableBalanceDetailService.remove(
                Wrappers.<CustomerReceivableBalanceDetail>lambdaQuery()
                        .eq(CustomerReceivableBalanceDetail::getBill_id, receivableBalance.getBill_id()));
        if (customerReceivableBalanceDetail == null && receivableBalance.getReceivable_balance().compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        //减去欠款
        subReceivableBalance(customer.getId(), customerReceivableBalanceDetail.getReceivable_balance());
        if (receivableBalance.getReceivable_balance().compareTo(BigDecimal.ZERO) != 0) {
            //新增欠款
            addReceivableBalanceDetails(receivableBalance);
        }
    }

    @Override
    public JSONArray listAllOnly() {
        List<Customer> supplierList = list();
        JSONArray res = new JSONArray();
        for (Customer supplier : supplierList) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.putOnce("id", supplier.getId());
            jsonObject.putOnce("name", supplier.getCustomerName());
            jsonObject.putOnce("paymentTerms", supplier.getPaymentTerms());
            res.add(jsonObject);
        }
        return res;
    }

    @Override
    public void checkCustomer(Long customerId) {
        Customer byId = getById(customerId);
        if (byId == null || byId.getStatus() == 0) throw new BillsException(MessageConstant.CUSTOMER_NOT_AVAILABLE);
    }
}
